using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;

namespace Pegasus.Diagnostics
{
	/// <summary>
	/// DebugCode class is a utility wrapper aound the <see cref="System.Diagnostics.Debug"/> class.
	/// This class provide default messages and format capibilites that the Debug class does not.
	/// </summary>
	[Obsolete( "Use Log4Net API in place of DebugCode", false )]
	public class DebugCode
	{
		// Local Static Values
		private static bool s_throwWriteExceptions = false;
		private static bool s_userInterfaceOnAssertFailure = true;

		/// <summary>
		/// 
		/// </summary>
		static DebugCode()
		{
			Debug.AutoFlush = true;
		}

		/// <summary>
		/// Gets/sets value indicating weather or no to throw exceptions received
		/// when trying to write to the debug stream.
		/// </summary>
		public static bool ThrowWriteExceptions
		{
			get
			{
				return s_throwWriteExceptions;
			}

			set
			{
				s_throwWriteExceptions = value;
			}
		}

		/// <summary>
		/// Gets/sets the indent level.
		/// </summary>
		public static int IndentLevel
		{
			get
			{
				return Debug.IndentLevel;
			}

			set
			{
				Debug.IndentLevel = value;
			}
		}

		/// <summary>
		/// Gets or sets the number of spaces in an indent.
		/// </summary>
		public static int IndentSize
		{
			get
			{
				return Debug.IndentSize;
			}
			
			set
			{
				Debug.IndentSize = value;
			}
		}

		/// <summary>
		/// Gets or sets a value indicating whether Flush should be called on the Listeners after every write.
		/// </summary>
		public static bool AutoFlush 
		{
			get
			{
				return Debug.AutoFlush;
			}
			
			set
			{
				Debug.AutoFlush = value;
			}
		}

		/// <summary>
		/// Gets or sets a value indicating whether a message box (user interface)
		/// should be shown for assert failures.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if a message box will be shown on assert failures; otherwise, <c>false</c>.
		/// </value>
		public static bool UserInterfaceOnAssertFailure
		{
			get
			{
				return s_userInterfaceOnAssertFailure;
			}

			set
			{
				s_userInterfaceOnAssertFailure = value;
			}
		}

		/// <summary>
		/// Gets the collection of listeners that is monitoring the debug output.
		/// </summary>
		public static TraceListenerCollection Listeners 
		{
			get
			{
				return Debug.Listeners;
			}
		}

		/// <summary>
		/// Emits the specified error message.
		/// </summary>
		/// <param name="messageFormat">A message to display containing zero or more format items.</param>
		/// <param name="args">An object array containing zero or more objects to format.</param>
		[ Conditional( "DEBUG" ) ]
		public static void Fail( string messageFormat, params object[] args )
		{
			AssertFailed( string.Format( messageFormat, args ) );
		}
		
		/// <summary>
		/// Checks for a condition and displays a message if the condition is <strong>false</strong>.
		/// </summary>
		/// <param name="condition">If false the message will be displayed; If true the method is skipped.</param>
		[ Conditional( "DEBUG" ) ]
		public static void Assert( bool condition )
		{
			if( !condition )
			{
				string msg = string.Format( "Assert failed at {0}", TraceDumper.GetCallingMethod( new StackFrame( 1, true ) ) );
				AssertFailed( msg );
			}
		}

		/// <summary>
		/// Checks for a condition and displays a message if the condition is <strong>false</strong>.
		/// </summary>
		/// <param name="condition">If false the message will be displayed; If true the method is skipped.</param>
		/// <param name="messageFormat">A message to display containing zero or more format items.</param>
		/// <param name="args">An object array containing zero or more objects to format.</param>
		[ Conditional( "DEBUG" ) ]
		public static void Assert( bool condition, string messageFormat, params object[] args )
		{
			if( !condition )
			{
				string msg = string.Format( messageFormat, args );
				AssertFailed( msg );
			}
		}

		/// <summary>
		/// Checks if the given object is null and and displays a message if it is.
		/// </summary>
		/// <param name="obj">The object to check.</param>
		[ Conditional( "DEBUG" ) ]
		public static void AssertNull( object obj )
		{
			if( obj != null )
			{
				string msg = string.Format( "Assert failed, object has a value, at {0}", TraceDumper.GetCallingMethod( new StackFrame( 1, true ) ) );
				AssertFailed( msg );
			}
		}

		/// <summary>
		/// Checks if the given object is null and and displays a message if it is.
		/// </summary>
		/// <param name="obj">The object to check.</param>
		/// <param name="messageFormat">A message to display containing zero or more format items.</param>
		/// <param name="args">An object array containing zero or more objects to format.</param>
		[ Conditional( "DEBUG" ) ]
		public static void AssertNull( object obj, string messageFormat, params object[] args )
		{
			if( obj != null )
			{
				string msg = string.Format( messageFormat, args );
				AssertFailed( msg );
			}
		}

		/// <summary>
		/// Checks if the given object is null and and displays a message if it is.
		/// </summary>
		/// <param name="obj">The object to check.</param>
		[ Conditional( "DEBUG" ) ]
		public static void AssertNotNull( object obj )
		{
			if( obj == null )
			{
				string msg = string.Format( "Assert failed, object is null, at {0}", TraceDumper.GetCallingMethod( new StackFrame( 1, true ) ) );
				AssertFailed( msg );
			}
		}

		/// <summary>
		/// Checks if the given object is null and and displays a message if it is.
		/// </summary>
		/// <param name="obj">The object to check.</param>
		/// <param name="messageFormat">A message to display containing zero or more format items.</param>
		/// <param name="args">An object array containing zero or more objects to format.</param>
		[ Conditional( "DEBUG" ) ]
		public static void AssertNotNull( object obj, string messageFormat, params object[] args )
		{
			if( obj == null )
			{
				string msg = string.Format( messageFormat, args );
				AssertFailed( msg );
			}
		}

		/// <summary>
		/// Checks if the given string is null or empty and and displays a message if it is.
		/// </summary>
		/// <param name="str">The string to check.</param>
		[ Conditional( "DEBUG" ) ]
		public static void AssertNotEmpty( string str )
		{
			if( str == null || str.Length == 0 )
			{
				string msg = string.Format( "Assert failed, string is null or empty, at {0}", TraceDumper.GetCallingMethod( new StackFrame( 1, true ) ) );
				AssertFailed( msg );
			}
		}

		/// <summary>
		/// Checks if the given string is null or empty and and displays a message if it is.
		/// </summary>
		/// <param name="str">The string to check.</param>
		/// <param name="messageFormat">A message to display containing zero or more format items.</param>
		/// <param name="args">An object array containing zero or more objects to format.</param>
		[ Conditional( "DEBUG" ) ]
		public static void AssertNotEmpty( string str, string messageFormat, params object[] args )
		{
			if( str == null || str.Length == 0 )
			{
				string msg = string.Format( messageFormat, args );
				AssertFailed( msg );
			}
		}

		/// <summary>
		/// Checks if the given Guid is empty and and displays a message if it is.
		/// </summary>
		/// <param name="guid">The Guid to check.</param>
		[ Conditional( "DEBUG" ) ]
		public static void AssertNotEmpty( Guid guid )
		{
			if( guid == Guid.Empty )
			{
				string msg = string.Format( "Assert failed, guid is empty, at {0}", TraceDumper.GetCallingMethod( new StackFrame( 1, true ) ) );
				AssertFailed( msg );
			}
		}

		/// <summary>
		/// Checks if the given Guid is empty and and displays a message if it is.
		/// </summary>
		/// <param name="guid">The Guid to check.</param>
		/// <param name="messageFormat">A message to display containing zero or more format items.</param>
		/// <param name="args">An object array containing zero or more objects to format.</param>
		[ Conditional( "DEBUG" ) ]
		public static void AssertNotEmpty( Guid guid, string messageFormat, params object[] args )
		{
			if( guid == Guid.Empty )
			{
				string msg = string.Format( messageFormat, args );
				AssertFailed( msg );
			}
		}

		/// <summary>
		/// Checks if the given IPAddress is empty and and displays a message if it is.
		/// </summary>
		/// <param name="ipAddress">The IPAddress to check.</param>
		[ Conditional( "DEBUG" ) ]
		public static void AssertNotEmpty( IPAddress ipAddress )
		{
			if( ipAddress == null || ipAddress == IPAddress.None )
			{
				string msg = string.Format( "Assert failed, IPAddress is none, at {0}", TraceDumper.GetCallingMethod( new StackFrame( 1, true ) ) );
				AssertFailed( msg );
			}
		}

		/// <summary>
		/// Checks if the given IPAddress is empty and and displays a message if it is.
		/// </summary>
		/// <param name="ipAddress">The IPAddress to check.</param>
		/// <param name="messageFormat">A message to display containing zero or more format items.</param>
		/// <param name="args">An object array containing zero or more objects to format.</param>
		[ Conditional( "DEBUG" ) ]
		public static void AssertNotEmpty( IPAddress ipAddress, string messageFormat, params object[] args )
		{
			if( ipAddress == null || ipAddress == IPAddress.None )
			{
				string msg = string.Format( messageFormat, args );
				AssertFailed( msg );
			}
		}

		/// <summary>
		/// Checks if the given DateTime is empty and and displays a message if it is.
		/// </summary>
		/// <param name="date">The DateTime to check.</param>
		[ Conditional( "DEBUG" ) ]
		public static void AssertNotEmpty( DateTime date )
		{
			if( date == DateTime.MinValue )
			{
				string msg = string.Format( "Assert failed, DateTime is 1/1/1, at {0}", TraceDumper.GetCallingMethod( new StackFrame( 1, true ) ) );
				AssertFailed( msg );
			}
		}

		/// <summary>
		/// Checks if the given DateTime is empty and and displays a message if it is.
		/// </summary>
		/// <param name="date">The DateTime to check.</param>
		/// <param name="messageFormat">A message to display containing zero or more format items.</param>
		/// <param name="args">An object array containing zero or more objects to format.</param>
		[ Conditional( "DEBUG" ) ]
		public static void AssertNotEmpty( DateTime date, string messageFormat, params object[] args )
		{
			if( date == DateTime.MinValue )
			{
				string msg = string.Format( messageFormat, args );
				AssertFailed( msg );
			}
		}

		/// <summary>
		/// Checks if the given int is between the min and max values and displays a message if it is not.
		/// </summary>
		/// <param name="value">Int value to check.</param>
		/// <param name="min">Minimum value.</param>
		/// <param name="max">Maximum value.</param>
		[ Conditional( "DEBUG" ) ]
		public static void AssertRange( int value, int min, int max )
		{
			if( value < min || value > max )
			{
				string msg = string.Format( "Assert failed, int out of range {0} < {1} < {2}, at {3}", 
					min, value, max,
					TraceDumper.GetCallingMethod( new StackFrame( 1, true ) ) );
				AssertFailed( msg );
			}
		}

		/// <summary>
		/// Checks if the given int is between the min and max values and displays a message if it is not.
		/// </summary>
		/// <param name="value">Int value to check.</param>
		/// <param name="min">Minimum value.</param>
		/// <param name="max">Maximum value.</param>
		/// <param name="messageFormat">A message to display containing zero or more format items.</param>
		/// <param name="args">An object array containing zero or more objects to format.</param>
		[ Conditional( "DEBUG" ) ]
		public static void AssertRange( int value, int min, int max, string messageFormat, params object[] args )
		{
			if( value < min || value > max )
			{
				string msg = string.Format( messageFormat, args );
				AssertFailed( msg );
			}
		}

		/// <summary>
		/// Checks if the given long is between the min and max values and displays a message if it is not.
		/// </summary>
		/// <param name="value">Long value to check.</param>
		/// <param name="min">Minimum value.</param>
		/// <param name="max">Maximum value.</param>
		[ Conditional( "DEBUG" ) ]
		public static void AssertRange( long value, long min, long max )
		{
			if( value < min || value > max )
			{
				string msg = string.Format( "Assert failed, long out of range {0} < {1} < {2}, at {3}", 
					min, value, max,
					TraceDumper.GetCallingMethod( new StackFrame( 1, true ) ) );
				AssertFailed( msg );
			}
		}

		/// <summary>
		/// Checks if the given long is between the min and max values and displays a message if it is not.
		/// </summary>
		/// <param name="value">Long value to check.</param>
		/// <param name="min">Minimum value.</param>
		/// <param name="max">Maximum value.</param>
		/// <param name="messageFormat">A message to display containing zero or more format items.</param>
		/// <param name="args">An object array containing zero or more objects to format.</param>
		[ Conditional( "DEBUG" ) ]
		public static void AssertRange( long value, long min, long max, string messageFormat, params object[] args )
		{
			if( value < min || value > max )
			{
				string msg = string.Format( messageFormat, args );
				AssertFailed( msg );
			}
		}

		/// <summary>
		/// Checks if the given double is between the min and max values and displays a message if it is not.
		/// </summary>
		/// <param name="value">Double value to check.</param>
		/// <param name="min">Minimum value.</param>
		/// <param name="max">Maximum value.</param>
		[ Conditional( "DEBUG" ) ]
		public static void AssertRange( double value, double min, double max )
		{
			if( value < min || value > max )
			{
				string msg = string.Format( "Assert failed, double out of range {0} < {1} < {2}, at {3}", 
					min, value, max,
					TraceDumper.GetCallingMethod( new StackFrame( 1, true ) ) );
				AssertFailed( msg );
			}
		}

		/// <summary>
		/// Checks if the given double is between the min and max values and displays a message if it is not.
		/// </summary>
		/// <param name="value">Double value to check.</param>
		/// <param name="min">Minimum value.</param>
		/// <param name="max">Maximum value.</param>
		/// <param name="messageFormat">A message to display containing zero or more format items.</param>
		/// <param name="args">An object array containing zero or more objects to format.</param>
		[ Conditional( "DEBUG" ) ]
		public static void AssertRange( double value, double min, double max, string messageFormat, params object[] args )
		{
			if( value < min || value > max )
			{
				string msg = string.Format( messageFormat, args );
				AssertFailed( msg );
			}
		}

		/// <summary>
		/// Dumps the exception to the debug stream.
		/// </summary>
		/// <param name="e">The exception to dump.</param>
		[ Conditional( "DEBUG" ) ]
		public static void Exception( Exception e )
		{
			try
			{
				Debug.WriteLine( TraceDumper.BuildDumpException( 2, e, null ) );
			}
			catch( Exception ex )
			{
				if( s_throwWriteExceptions )
				{
					throw new Exception( "Debug write exception", ex );
				}
			}
		}

		/// <summary>
		/// Dumps the exception to the debug stream.
		/// </summary>
		/// <param name="e">The exception to dump.</param>
		/// <param name="messageFormat">A message to display containing zero or more format items.</param>
		/// <param name="args">An object array containing zero or more objects to format.</param>
		[ Conditional( "DEBUG" ) ]
		public static void Exception( Exception e, string messageFormat, params object[] args )
		{
			try
			{
				Debug.WriteLine( TraceDumper.BuildDumpException( 2, e, messageFormat, args ) );
			}
			catch( Exception ex )
			{
				if( s_throwWriteExceptions )
				{
					throw new Exception( "Debug write exception", ex );
				}
			}
		}

		/// <summary>
		/// Dumps the exception to the trace stream.
		/// </summary>
		/// <param name="condition">If true the message will be displayed; If false the method is skipped.</param>
		/// <param name="e">The exception to dump.</param>
		[ Conditional( "DEBUG" ) ]
		public static void ExceptionIf( bool condition, Exception e )
		{
			try
			{
				if( condition )
				{
					Debug.WriteLine( TraceDumper.BuildDumpException( 2, e, null ) );
				}
			}
			catch( Exception ex )
			{
				if( s_throwWriteExceptions )
				{
					throw new Exception( "Debug write exception", ex );
				}
			}
		}

		/// <summary>
		/// Dumps the exception to the trace stream.
		/// </summary>
		/// <param name="condition">If true the message will be displayed; If false the method is skipped.</param>
		/// <param name="e">The exception to dump.</param>
		/// <param name="messageFormat">A message to display containing zero or more format items.</param>
		/// <param name="args">An object array containing zero or more objects to format.</param>
		[ Conditional( "DEBUG" ) ]
		public static void ExceptionIf( bool condition, Exception e, string messageFormat, params object[] args )
		{
			try
			{
				if( condition )
				{
					Debug.WriteLine( TraceDumper.BuildDumpException( 2, e, messageFormat, args ) );
				}
			}
			catch( Exception ex )
			{
				if( s_throwWriteExceptions )
				{
					throw new Exception( "Debug write exception", ex );
				}
			}
		}

		/// <summary>
		/// Displays a message on the debug stream.
		/// </summary>
		/// <param name="messageFormat">A message to display containing zero or more format items.</param>
		/// <param name="args">An object array containing zero or more objects to format.</param>
		[ Conditional( "DEBUG" ) ]
		public static void Write( string messageFormat, params object[] args )
		{
			try
			{
				string msg = string.Format( messageFormat, args );
				Debug.Write( msg );
			}
			catch( Exception ex )
			{
				if( s_throwWriteExceptions )
				{
					throw new Exception( "Debug write exception", ex );
				}
			}
		}

		/// <summary>
		/// Write just a new line to the output streams
		/// </summary>
		[ Conditional( "DEBUG" ) ]
		public static void WriteLine()
		{
			try
			{
				Debug.WriteLine( string.Empty );
			}
			catch( Exception ex )
			{
				if( s_throwWriteExceptions )
				{
					throw new Exception( "Debug write exception", ex );
				}
			}
		}

		/// <summary>
		/// Displays a message on the debug stream.
		/// </summary>
		/// <param name="messageFormat">A message to display containing zero or more format items.</param>
		/// <param name="args">An object array containing zero or more objects to format.</param>
		[ Conditional( "DEBUG" ) ]
		public static void WriteLine( string messageFormat, params object[] args )
		{
			try
			{
				string msg = string.Format( messageFormat, args );
				Debug.WriteLine( msg );
			}
			catch( Exception ex )
			{
				if( s_throwWriteExceptions )
				{
					throw new Exception( "Debug write exception", ex );
				}
			}
		}

		/// <summary>
		/// Checks for a condition and displays a message if the condition is true.
		/// </summary>
		/// <param name="condition">If true the message will be displayed; If false the method is skipped.</param>
		/// <param name="messageFormat">A message to display containing zero or more format items.</param>
		/// <param name="args">An object array containing zero or more objects to format.</param>
		[ Conditional( "DEBUG" ) ]
		public static void WriteIf( bool condition, string messageFormat, params object[] args )
		{
			try
			{
				if( condition )
				{
					string msg = string.Format( messageFormat, args );
					Debug.Write( msg );
				}
			}
			catch( Exception ex )
			{
				if( s_throwWriteExceptions )
				{
					throw new Exception( "Debug write exception", ex );
				}
			}
		}

		/// <summary>
		/// Checks for a condition and displays a message if the condition is true.
		/// </summary>
		/// <param name="condition">If true the message will be displayed; If false the method is skipped.</param>
		/// <param name="messageFormat">A message to display containing zero or more format items.</param>
		/// <param name="args">An object array containing zero or more objects to format.</param>
		[ Conditional( "DEBUG" ) ]
		public static void WriteLineIf( bool condition, string messageFormat, params object[] args )
		{
			try
			{
				if( condition )
				{
					string msg = string.Format( messageFormat, args );
					Debug.WriteLine( msg );
				}
			}
			catch( Exception ex )
			{
				if( s_throwWriteExceptions )
				{
					throw new Exception( "Debug write exception", ex );
				}
			}
		}

		/// <summary>
		/// Flushes the output buffer and causes buffered data to write to the Listeners collection.
		/// </summary>
		[ Conditional( "DEBUG" ) ]
		public static void Flush()
		{
			try
			{
				Debug.Flush();
			}
			catch( Exception ex )
			{
				if( s_throwWriteExceptions )
				{
					throw new Exception( "Debug write exception", ex );
				}
			}
		}

		/// <summary>
		/// Increases the current IndentLevel by one.
		/// </summary>
		[ Conditional( "DEBUG" ) ]
		public static void Indent()
		{
			try
			{
				Debug.Indent();
			}
			catch( Exception ex )
			{
				if( s_throwWriteExceptions )
				{
					throw new Exception( "Debug write exception", ex );
				}
			}
		}

		/// <summary>
		/// Decreases the current IndentLevel by one.
		/// </summary>
		[ Conditional( "DEBUG" ) ]
		public static void Unindent()
		{
			try
			{
				Debug.Unindent();
			}
			catch( Exception ex )
			{
				if( s_throwWriteExceptions )
				{
					throw new Exception( "Debug write exception", ex );
				}
			}
		}

		/// <summary>
		/// Asserts the failed.
		/// </summary>
		/// <param name="message">The message.</param>
		private static void AssertFailed( string message )
		{
			if( s_userInterfaceOnAssertFailure )
			{
				Debug.Fail( message );
			}
			else
			{
				WriteLine( message );
			}
		}
	}
}
